#include "../header/Server.h"

int Server::id = -1;

ThreadPool *Server::pool = nullptr;

bool Server::isCompleteShunt = false;

pthread_mutex_t Server::mutex = PTHREAD_MUTEX_INITIALIZER;

Server::~Server() {
    endServer();
    pthread_mutex_destroy(&mutex);
}

void Server::startServer() {
    // 创建 Data 文件, 若 Data 文件存在就进行任何操作
    createDataFile();

    if (!NetworkTools::isServerPortUse()) {
        pthread_mutex_lock(&mutex);
        if (!NetworkTools::isServerPortUse()) {
            printf("Start Address Book Server!\n");
            pool = new ThreadPool(60, 3, 8, new queue<Task>(), new SimpleThreadFactory());
            pool->submitTask(pool, Server::serverWorkThread);
        }
        pthread_mutex_unlock(&mutex);

    } else {
        printf("The Port Is Occupied!\n");
        pthread_exit(nullptr);
    }
}

void Server::endServer() {
    if (!isCompleteShunt) {
        if (NetworkTools::isServerPortUse()) {
            delete pool;
            isCompleteShunt = true;
        }
    }
}

int Server::getUserId() {
    int userId;
    pthread_mutex_lock(&mutex);
    userId = Server::id;
    if (userId == -1) {
        userId = Server::id = getUserIdFromFile();
    }

    if (userId != -1) {
        Server::id++;
        saveUserIdToFile(Server::id);
    }
    pthread_mutex_unlock(&mutex);

    return userId;
}

void Server::createDataFile() {
    string file_dir_pathname = "C:\\ProgramData\\Blue";
    mkdir(file_dir_pathname.c_str(), 0777);
    file_dir_pathname += "\\Address Book";
    mkdir(file_dir_pathname.c_str(), 0777);

    string file_pathname = string(PROJECT_DIR_PATHNAME) + "\\Data.txt";
    FILE *file = fopen(file_pathname.c_str(), "r");
    if (file == nullptr) {
        ofstream ofs = ofstream();

        ofs.open(file_pathname, ios::out);
        ofs << "ID:100001" << endl;

        ofs.close();
    }
}

int Server::getUserIdFromFile() {
    int userId = -1;
    ifstream ifs = ifstream();
    string file_pathname = string(PROJECT_DIR_PATHNAME) + "\\Data.txt";
    ifs.open(file_pathname, ios::out);

    if (ifs.is_open()) {
        string buffer;
        while (getline(ifs, buffer)) {
            if (buffer.find("ID") != -1) {
                userId = atoi(buffer.substr(3).c_str());
            }
        }
    } else {
        printf("Open File 'Data.txt' Fail\n");
    }

    return userId;
}

bool Server::saveUserIdToFile(int userId) {
    ofstream ofs = ofstream();
    string file_pathname = string(PROJECT_DIR_PATHNAME) + "\\Data.txt";
    ofs.open(file_pathname, ios::out);

    if (ofs.is_open()) {
        ofs << "ID:" + to_string(userId);
        ofs.close();

        return true;
    }

    printf("Write File 'Data.txt' Fail!\n");
    return false;
}

void Server::changeIntToChar(int userId, int &length, char buffer[]) {
    memset(buffer, 0, 1024);
    string message = to_string(userId);
    string public_key = NetworkTools::encryptionMessage(message);
    length = public_key.size();
    for (int i = 0; i < length; ++i) {
        buffer[i] = public_key[i];
    }
}

void Server::serverWorkThread(void *args) {
    // 1.获取线程池对象指针
    ThreadPool *pool = static_cast<ThreadPool *>(args);

    // 2.创建监听的套接字
    int server_fd = -1;
    pool->server_fd = server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    if (server_fd == -1) {
        perror("Socket Create Fail!\n");
        return;
    }

    // 3.绑定本地的 IP 和 PORT
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(SERVER_PORT);
    server_address.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(server_fd, (struct sockaddr *) &server_address, sizeof(server_address));
    if (ret == -1) {
        close(server_fd);
        perror("IP And PORT Bind Fail!\n");
        return;
    }

    // 4.设置监听
    ret = listen(server_fd, 128);
    if (ret == -1) {
        close(server_fd);
        perror("Listen Fail!\n");
    }

    // 5.将等待客户端连接的任务提交到线程池中
    pair<ThreadPool *, int> *thread_pool_pointer_and_server_pid = new pair<ThreadPool *, int>(pool, server_fd);
    pool->submitTask(thread_pool_pointer_and_server_pid, Server::serverAcceptTaskThread);
    printf("Server Start Success, The Local Port %d Is Bound Successfully!\n", SERVER_PORT);
    pthread_exit(nullptr);
}

void Server::serverAcceptTaskThread(void *args) {
    // 1.获取线程池对象指针和服务端文件描述符
    pair<ThreadPool *, int> thread_pool_pointer_and_server_pid = *static_cast<pair<ThreadPool *, int> *>(args);
    ThreadPool *pool = thread_pool_pointer_and_server_pid.first;
    int server_fd = thread_pool_pointer_and_server_pid.second;

    while (true) {
        // 2.阻塞并等待客户端连接
        struct sockaddr_in client_address;
        int client_sock_length = sizeof(client_address);
        int client_fd = accept(server_fd, (struct sockaddr *) &client_address, &client_sock_length);
        if (client_fd == -1) {
            close(server_fd);
            perror("Accept Fail!\n");
            return;
        }

        // 3.与客户端建立连接成功, 打印客户端的 IP 和 PID
        char client_ip[32];
        printf("Client IP: %s, Client PID: %d",
               inet_ntop(AF_INET, &client_address.sin_addr, client_ip, sizeof(client_ip)),
               ntohs(client_address.sin_port));

        // 4.将与客户端通信的任务提交到线程池中
        pair<int, struct sockaddr_in> *client_fd_and_client_address = new pair<int, struct sockaddr_in>(client_fd,
                                                                                                        client_address);
        pool->submitTask(client_fd_and_client_address, Server::serverCommunicateTaskThread);
    }
}

void Server::serverCommunicateTaskThread(void *args) {
    // 1.获取客户端文件描述符和客户端 IP 地址相关的信息
    pair<int, struct sockaddr_in> client_fd_and_client_address = *static_cast< pair<int, struct sockaddr_in> *>(args);
    int client_fd = client_fd_and_client_address.first;
    struct sockaddr_in client_address = client_fd_and_client_address.second;

    // 2.与客户端通信
    while (true) {
        char buffer[1024];
        int length = recv(client_fd, buffer, sizeof(buffer), 0);

        if (0 < length) {
            string public_key = buffer;
            printf("public_key = %s\n", buffer);

            if (!NetworkTools::decryptionMessage(public_key).empty()) {
                string private_key = NetworkTools::decryptionMessage(public_key);
                printf("Public Key Verify Success!\n");
                printf("Private Key Is: %s\n", public_key.c_str());

                int userId = Server::getUserId();
                printf("New User ID Is %d\n", userId);

                // 将新用户的 int(ID) 加密转为 char*(ID) 后发送给客户端
                changeIntToChar(userId, length, buffer);
                send(client_fd, buffer, length, 0);

            } else {
                send(client_fd, buffer, length, 0);
                printf("Public Key Verify Fail!\n");
            }

        } else if (0 == length) {
            printf("The Client Is Disconnect!\n");
            break;

        } else {
            close(client_fd);
            printf("Receive Server Data Fail!\n");
            return;
        }
    }

    // 4.关闭客户端文件描述符
    close(client_fd);
}

